package fr.generali.accueilclient.gwt.client.dispatch;

import java.util.Set;

import net.customware.gwt.dispatch.client.secure.SecureDispatchService;
import net.customware.gwt.dispatch.client.secure.SecureDispatchServiceAsync;
import net.customware.gwt.dispatch.client.secure.SecureSessionAccessor;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;
import net.customware.gwt.dispatch.shared.secure.InvalidSessionException;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.web.bindery.event.shared.EventBus;

import fr.generali.accueilclient.gwt.client.dispatch.event.ActionCallbackedEvent;
import fr.generali.accueilclient.gwt.client.dispatch.event.ActionDispatchedEvent;

public class GeneraliSecureDispatchAsync implements GeneraliDispatchAsync {

    private static final SecureDispatchServiceAsync realService = GWT.create(SecureDispatchService.class);

    private final SecureSessionAccessor secureSessionAccessor;

    private final DispatchUrlBuilder urlBuilder;

    private final Set<ActionExceptionHandler> exceptionHandlers;

    private final EventBus eventBus;

    public GeneraliSecureDispatchAsync(Set<ActionExceptionHandler> exceptionHandler, SecureSessionAccessor secureSessionAccessor, EventBus eventBus) {
        this(exceptionHandler, new DefaultDispatchUrlBuilder(), secureSessionAccessor, eventBus);
    }

    public GeneraliSecureDispatchAsync(Set<ActionExceptionHandler> exceptionHandler, DispatchUrlBuilder urlBuilder,
                    SecureSessionAccessor secureSessionAccessor, EventBus eventBus) {
        this.exceptionHandlers = exceptionHandler;
        this.secureSessionAccessor = secureSessionAccessor;
        this.urlBuilder = urlBuilder;
        this.eventBus = eventBus;
    }

    @Override
    public <A extends Action<R>, R extends Result> void execute(final A action, final AsyncCallback<R> callback) {
        execute(action, callback, true);
    }

    @Override
    public <A extends Action<R>, R extends Result> void execute(final A action, final AsyncCallback<R> callback, final boolean modal) {
        String sessionId = secureSessionAccessor.getSessionId();

        addActionNameInUrlToFacilitateLisibility(action);

        if (modal) {
            eventBus.fireEvent(new ActionDispatchedEvent());
        }

        realService.execute(sessionId, action, new AsyncCallback<Result>() {

            @SuppressWarnings("unchecked")
            @Override
            public void onSuccess(Result result) {
                if (modal) {
                    eventBus.fireEvent(new ActionCallbackedEvent());
                }
                callback.onSuccess((R ) result);
            }

            @Override
            public void onFailure(Throwable caught) {
                if (modal) {
                    eventBus.fireEvent(new ActionCallbackedEvent());
                }

                if (caught instanceof InvalidSessionException) {
                    secureSessionAccessor.clearSessionId();
                }

                for (ActionExceptionHandler handler : exceptionHandlers) {
                    if (handler.onFailure(action, caught, callback) == ActionExceptionHandler.Status.STOP) {
                        return;
                    }
                }

                callback.onFailure(caught);
            }

        });
    }

    public <A extends Action<R>, R extends Result> void addActionNameInUrlToFacilitateLisibility(final A action) {
        ((ServiceDefTarget ) realService).setServiceEntryPoint(urlBuilder.buildURL(action));
    }
}
